package com.toutiao.nssue

import scala.math.sqrt

import org.apache.log4j.Logger

import scala.collection.mutable.ArrayBuffer
import scala.collection.mutable.{HashMap => HMap}

import com.toutiao.issue.SimpleDate
import com.toutiao.issue.WAND

import com.toutiao.score.{Topic,SimScorer,SimpleFactory}
import com.toutiao.model.{Converter,TimeIndex,TimeDict,TimeDB}

import com.twitter.util.{SynchronizedLruMap => LruCache}

class NssTitleCollector(
		val minS: Double = 3.0,
		val mIndex: TimeIndex,
		val mTime: TimeDict,
		val mDB: TimeDB
    ) {
  
  private
  val _log = Logger.getLogger(this.getClass.getName)
  
  private
  val simScorer = new SimScorer
  
  def get(n:String) = {
    if (mTime.isIn(n)) {
    	val t = mTime(n)
    	val m = mDB(t)(n)
    	m      
    }
    else {
      new Topic
    }
  }
  
  private
  def norm(vs:Map[String,Double]) = {
    SimpleFactory.norm(vs)
  }
  
  private
  def merge(m1:Map[String,Double],n1:Int,m2:Map[String,Double],n2:Int) = {
    SimpleFactory.merge(m1,n1,m2,n2)
  }
  
  private
  def asTopic(x:Document) = {
    //new Topic(x.name,Array(x.name),norm(x.vs),x.title,1.0,x.date,x.date)
    //new Topic(x.name,Array(x.name),x.vs,x.title,x.vs.size.toDouble)
    val t = new Topic
    t.i = x.name
    t.v = norm(x.vs)
    t.s = 1.0
    val ms = Array(x.name)
    val title = x.title
    val cc = 3
    val image =
      if (x.image.size > cc)
        x.image.slice(0, cc)
      else
        x.image
    t.c = x.date
    t.u = x.date
    t.m = Map("m"->ms,"t"->title,"e"->image)
    t
  }
  
  def insert(name:String,i:Long,topic:Topic) {
    val t = SimpleDate.format(topic.c).split(" ")(0)
    mDB.insert(t, name, topic)
    mIndex.insert(t, i, topic.m("t"))
    mTime.insert(name, t)
  }
  
  def remove(t:String) = { // 天为单位
    mIndex.remove(t)
    mDB.remove(t)
    mTime.removeTime(t)
  }
  
  private
  def retrieve(vs:Map[String,Double],S:Double) = {
    val iis = mIndex.getIndex
    val es = iis.map(index => {
      new WAND(index,vs,S)
    })
    
    val ns = es.par.flatMap(e => e.retrieve).seq
    ns
  }
  
  private
  val CACHE = new LruCache[String,Topic](65536*16)
  
  private def getFeature(ns:Array[String]) = {
    val nns = ns.filter(!CACHE.contains(_))
    val xxs = ns.filter(CACHE.contains(_)).map(n => {(n,CACHE(n))}) // Hit Cache
    val xs = nns.map(n => {
      val t = mTime(n)
      (n, mDB(t).getValue(n))
    })
    
    val xData = xs.par.map(x => {
      val n = x._1
      //val v = Converter.parseLine(x._2)
      val v = Converter.parseData(x._2)
      (n,v)
    }).seq.toMap
    
    xData.foreach( nv => { // Update Cache
    	if (nv._2.m("m").size > 1)
    	  CACHE(nv._1) = nv._2
      }
    )
    
    xData++xxs
  }
    
  private def getData(n:String) = {
    if (CACHE.contains(n)){
      CACHE(n)
    }
    else {
      val t = mTime(n)
      val x = mDB(t).getValue(n)
      Converter.parseData(x)
    }
  }
  
  
  // 
  def process(
		  M1:HMap[String,Long],
		  M2:HMap[Long,String],
		  x:Document,
		  S:Double
      ) = {
    
    val topic = asTopic(x)
    
    //_log.info("Retrieve ...")
    val ns = retrieve(
        topic.m("t").filter(_.size > 1).map(v => { // 不对单字做召回
        	(v,1.0)
        	}).toMap, minS).
        	filter(M2.contains(_)).map(M2(_)).toArray
    //_log.info("Retrieve Done")
    
    val xData = getFeature(ns)
    
    val rs = ns.par.map( n => {
      (n,simScorer.getSimScore(xData(n),topic,S))
    }).seq.filter( r => {
      val n = r._1
      xData(n).m("m").size < 10000
    })

    val (z,s) = if (rs.size > 0) {
    				rs.maxBy(_._2)
    			}
    			else {
    				("",0.0)
    			}
    
    if (s == S+0.01 || s == S+0.02 || s == S+0.03){
    	_log.info(topic.i+" "+z+" "+s)  
    }
    
    if (s >= S){ // Found Similar
      val t = mTime(z)
      val topicD = xData(z)
      val vs = topicD.v
      val g = topicD.s
      val ms = topicD.m("m") ++ topic.m("m")
      val title = topicD.m("t")
      val image = topicD.m("e")
      
      val ct = 
        if (topicD.c.getTime < topic.c.getTime) topicD.c
        else topic.c
      val ut =
        if (topicD.u.getTime < topic.u.getTime) topic.u
        else topicD.u
        
      val tp = new Topic
      tp.i = z
      tp.v = vs
      tp.s = g
      tp.c = ct
      tp.u = ut
      tp.m = Map("m"->ms,"t"->title,"e"->image)
      //data += z->(new Topic(z,ms,vs,title,g,ct,ut))
      mDB.insert(t,z,tp)
      //mDB.insert(t,z,(new Topic(z,ms,vs,title,g,ct,ut)))
      // mIndex, mTime 不更新
      
      CACHE(z) = tp
      
      z
    }
    else {
      val i = M1(topic.i)
      insert(topic.i,i,topic)
      
      CACHE(topic.i) = topic
      
      topic.i
    }
    
  }
  
}